package com.sharesrc.jdev.wiimotedemo;

import java.io.IOException;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;

import org.wiigee.device.Wiimote;
import org.wiigee.filter.DirectionalEquivalenceFilter;
import org.wiigee.filter.HighPassFilter;
import org.wiigee.filter.IdleStateFilter;
import org.wiigee.filter.MotionDetectFilter;
import org.wiigee.filter.RotationThresholdFilter;
import org.wiigee.logic.Gesture;
import org.wiigee.logic.GestureModel;

import com.sharesrc.jdev.wiimotedemo.devices.WiimoteDevice;
import com.sharesrc.jdev.wiimotedemo.listener.IAccelerationListener;
import com.sharesrc.jdev.wiimotedemo.listener.IButtonListener;
import com.sharesrc.jdev.wiimotedemo.listener.IRotationListener;
import com.sharesrc.jdev.wiimotedemo.views.DetailedInfo;
import com.sharesrc.jdev.wiimotedemo.views.MainFrame;

public enum Wiigee implements IButtonListener, IAccelerationListener, IRotationListener {

	INSTANCE;

	WiimoteDevice wiimote;
	WiiUseJToWiigeeEventsDispatcher notifier = WiiUseJToWiigeeEventsDispatcher.INSTANCE;
	MainFrame gui;
	XProcessingUnit processingUnit;
	boolean isUpdateTrainingMonitorEnabled;
	AtomicInteger accelerationReceivedCount;
	AtomicInteger trainingCount;

	public WiimoteDevice getWiimote() {
		return this.wiimote;
	}

	public boolean isUpdateTrainingMonitorEnabled() {
		return this.isUpdateTrainingMonitorEnabled;
	}

	public void setUpdateTrainingMonitorEnabled(boolean isUpdateTrainingMonitorEnabled) {
		this.isUpdateTrainingMonitorEnabled = isUpdateTrainingMonitorEnabled;
	}

	private Wiigee() {
		this.isUpdateTrainingMonitorEnabled = false;
		this.accelerationReceivedCount = new AtomicInteger();
		this.trainingCount = new AtomicInteger();
	}

	public void init(MainFrame gui) {
		this.gui = gui;
		this.processingUnit = new XProcessingUnit(gui);
		initWiimote();
	}

	private void initWiimote() {
		try {
			this.wiimote = new WiimoteDevice("", true, false);
			setupWiimote();
			registerListeners();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void setupWiimote() {
		try {
			this.wiimote.setAccelerationEnabled(true);
			this.wiimote.addAccelerationFilter(new HighPassFilter());
			this.wiimote.addRotationFilter(new RotationThresholdFilter(0.5));
		} catch (Exception e) {
			e.printStackTrace();
		}
		this.wiimote.addAccelerationListener(this.processingUnit);
		this.wiimote.addButtonListener(this.processingUnit);
	}

	private void registerListeners() {
		this.notifier.addWiigeeButtonListener(this);
		this.notifier.addWiigeeAccelerationListener(this);
		this.notifier.addWiigeeRotationListener(this);
	}

	public void disconnectWiimote() {
		if (this.wiimote != null) {
			this.wiimote.disconnect();
		}
	}

	public Vector<Gesture> getRecoredTrainingData() {
		return this.processingUnit.getTrainsequence();
	}

	public Vector<GestureModel> getGestureModels() {
		return this.processingUnit.getClassifier().getGestureModels();
	}

	public void loadGesture(String filename) {
		this.processingUnit.loadGesture(filename);
	}

	@Override
	public void rotationReceived(double pitch, double roll, double yaw) {
		this.wiimote.fireRotationEvent(pitch, roll, yaw);
	}

	@Override
	public void accelerationReceived(double[] accelerationData) {
		this.wiimote.fireAccelerationEvent(accelerationData);

		if (!this.isUpdateTrainingMonitorEnabled) {
			return;
		}

		this.accelerationReceivedCount.incrementAndGet();

		String rawData = getAccelerationDataString(accelerationData);

		IdleStateFilter idleStateFilter = new IdleStateFilter();
		accelerationData = idleStateFilter.filter(accelerationData);
		String dataFiltedByIdleStateFilter = getAccelerationDataString(accelerationData);

		MotionDetectFilter motionDetectFilter = new MotionDetectFilter(this.wiimote);
		accelerationData = motionDetectFilter.filter(accelerationData);
		String dataFiltedByMotionDetectFilter = getAccelerationDataString(accelerationData);

		DirectionalEquivalenceFilter directionalEquivalenceFilter = new DirectionalEquivalenceFilter();
		accelerationData = directionalEquivalenceFilter.filter(accelerationData);
		String dataFiltedByDirectionalEquivalenceFilter = getAccelerationDataString(accelerationData);

		HighPassFilter highPassFilter = new HighPassFilter();
		accelerationData = highPassFilter.filter(accelerationData);
		String dataFiltedByHighPassFilter = getAccelerationDataString(accelerationData);

		String absoluteValue = "";
		if (accelerationData != null) {
			absoluteValue = String.valueOf(Math.sqrt((accelerationData[0] * accelerationData[0]) + (accelerationData[1] * accelerationData[1]) + (accelerationData[2] * accelerationData[2])));
		}

		DetailedInfo detailedInfo = new DetailedInfo(rawData, dataFiltedByIdleStateFilter, dataFiltedByMotionDetectFilter, dataFiltedByDirectionalEquivalenceFilter, dataFiltedByHighPassFilter, absoluteValue);
		this.gui.updateTrainingMonitorPanel(detailedInfo);
	}

	public int getAccelerationReceivedCount() {
		return this.accelerationReceivedCount.get();
	}

	public void resetAccelerationReceivedCount() {
		this.accelerationReceivedCount.set(0);
	}

	private String getAccelerationDataString(double[] accelerationData) {
		return new String("x = " + accelerationData[0] + ", y = " + accelerationData[1] + ", z = " + accelerationData[2]);
	}

	@Override
	public void buttonPressReceived(int button) {
		this.wiimote.fireButtonPressedEvent(button);
		if (button == Wiimote.BUTTON_MINUS) {
			this.wiimote.resetAccelerationFilters();
		} else if (button == Wiimote.BUTTON_PLUS) {
			this.wiimote.resetRotationFilters();
		}
	}

	@Override
	public void buttonReleaseReceived(int button) {
		this.wiimote.fireButtonReleasedEvent(button);
	}
}
